package com.ruicar.afs.cloud.batch.monthend.process;

import cn.hutool.core.date.DateUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSONObject;
import com.ruicar.afs.cloud.batch.dayend.handle.run.SystemJdbcConfig;
import com.ruicar.afs.cloud.batch.frame.AbsBatchRunner;
import com.ruicar.afs.cloud.common.config.VoucherConstant;
import com.ruicar.afs.cloud.common.core.enums.AfsEnumUtil;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.common.core.uid.UidGenerator;
import com.ruicar.afs.cloud.common.core.util.SpringContextHolder;
import com.ruicar.afs.cloud.common.modules.constant.VoucherBuriedPointNo;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.sql.*;


/**
 * @author infinity
 * @version 1.0
 * @date 2020/8/18 11:48
 */
@Slf4j
public class VoucherMonthlySettlementBathRunner extends AbsBatchRunner {
    private SystemJdbcConfig systemJdbcConfig = SpringContextHolder.getBean( SystemJdbcConfig.class );
    private UidGenerator uidGenerator = SpringContextHolder.getBean( UidGenerator.class );

    @Override
    public void process(String param) {
        //发送凭证信息
        log.info( "月结凭证信息--开始" );
        boolean isFinish = false;
        Connection contract = this.getConnection( systemJdbcConfig.getBasic() );
        /***批量日期**/
        Date batchDate = new Date( batchDateInfo.getBatchDate().getTime() );
        while (!isFinish) {
            String loadBreakPoint = loadBreakPoint();
            logger.info( "读取==={}", loadBreakPoint );
            isFinish = handleContractNo( loadBreakPoint, contract, batchDate );
        }
        log.info( "月结凭证信息--结束" );
    }

    private boolean handleContractNo(String loadBreakPoint, Connection contract, Date batchDate) {
        Long id = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        PreparedStatement psf = null;
        PreparedStatement updatePsf = null;
        try {
            id = getId( loadBreakPoint, contract );
            //查询需要生成月结凭证的数据
            StringBuffer sql = new StringBuffer();
            sql.append( "  select info.current_absolute_overdue,agreement.id ,agreement.is_sub_product,plan.due_date,plan.income as receivable_interest  , " );
            sql.append( "  agreement.cost_type,agreement.discount_type,plan.term_no,plan.contract_no,agreement.suspense_account as  outer_amt, " );
            sql.append( "  agreement.interest_income from  basic_interest_amortize_plan plan " );
            sql.append( "  inner join basic_debit_info info on info.contract_no=plan.contract_no " );
            sql.append( "  inner join basic_main_info main on main.contract_no=plan.contract_no and main.contract_status in('contractEffective','overdue') " );
            sql.append( "  inner join basic_financial_agreement agreement on agreement.contract_no=plan.contract_no " );
            sql.append( "  where  agreement.cost_type = 'CAR_AMT' and  DATE_FORMAT(plan.due_date,'%Y-%m')  = DATE_FORMAT(?,'%Y-%m') " );
            if (id != null) {
                sql.append( "  and  agreement.id>? order by agreement.id asc  limit 0, ?  " );
            } else {
                sql.append( "  order by agreement.id asc  limit 0, ?   " );
            }
            //核算明细
            StringBuffer saveFinanceBusinessAccounting = new StringBuffer();
            saveFinanceBusinessAccounting.append( "insert into basic_business_accounting(id,trans_id,fee_type,buried_point_no,amount,keep_account_date,contract_no,send_status,create_by,create_time  ) " );
            saveFinanceBusinessAccounting.append( "values(?,?,?,?,?,?,?,?,?,?)" );
            //更新暂记账
            StringBuffer updateSuspenseAccount = new StringBuffer();
            updateSuspenseAccount.append( " update   basic_financial_agreement  set suspense_account=ifnull(suspense_account,0)+? ,special_temporary=ifnull(special_temporary,0)+?,interest_income =ifnull(interest_income,0)+? where contract_no=?  and cost_type = 'CAR_AMT' " );

            ps = contract.prepareStatement( sql.toString() );
            ps.setTimestamp( 1, DateUtil.endOfDay( batchDateInfo.getBatchDate() ).toTimestamp() );
            if (id != null) {
                ps.setLong( 2, id );
                ps.setInt( 3, VoucherConstant.batchesSize );
            } else {
                ps.setInt( 2, VoucherConstant.batchesSize );
            }
            rs = ps.executeQuery();
            psf = contract.prepareStatement( saveFinanceBusinessAccounting.toString() );
            updatePsf = contract.prepareStatement( updateSuspenseAccount.toString() );
            if (rs.next()) {
                rs.previous();
            } else {
                return Boolean.TRUE;
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put( "startId", id );
            Long transId = null;
            while (rs != null && rs.next()) {
                id = rs.getLong( "id" );
                transId = handleNoSubProductAndCar( rs, psf, updatePsf, contract, batchDate );
            }
            psf.executeBatch();
            updatePsf.executeBatch();
            jsonObject.put( "endId", id );
            jsonObject.put( "targetId", transId );
            saveBreakPoint( jsonObject.toString() );
            contract.commit();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AfsBaseException( e.getMessage() );
        } finally {
            clean( ps );
            clean( rs );
            clean( psf );
        }
        return Boolean.FALSE;
    }

    //保存利息收入
    private void handleInterestIncome(PreparedStatement psf, Long transId, String type, String contractNo, BigDecimal receivableInterest, String voucherBuriedPointNo, Date batchDate) throws SQLException {
        if (receivableInterest == null || receivableInterest.compareTo( BigDecimal.ZERO ) == 0) return;
        psf.setLong( 1, transId );
        psf.setLong( 2, transId );
        psf.setString( 3, type );
        psf.setString( 4, AfsEnumUtil.key( voucherBuriedPointNo ) );
        psf.setBigDecimal( 5, receivableInterest );
        psf.setDate( 6, batchDate );
        psf.setString( 7, contractNo );
        psf.setString( 8, "notSend" );
        psf.setString( 9, "system" );
        psf.setDate( 10, batchDate );
        psf.addBatch();

    }

    //更新暂记账
    private void handleSuspenseAccount(PreparedStatement updatePsf, String contractNo, BigDecimal suspenseAccount,BigDecimal ninetySuspenseAccount,BigDecimal receivableInterest) throws SQLException {
        if (suspenseAccount == null || suspenseAccount.compareTo( BigDecimal.ZERO ) == 0) return;
        updatePsf.setBigDecimal( 1, suspenseAccount );
        updatePsf.setBigDecimal( 2, ninetySuspenseAccount);
        updatePsf.setBigDecimal( 3, receivableInterest);
        updatePsf.setString( 4, contractNo );
        updatePsf.addBatch();
    }

    //无子产品且费用类型为车款
    private Long handleNoSubProductAndCar(ResultSet rs, PreparedStatement psf, PreparedStatement updatePsf, Connection contract, Date batchDate) throws SQLException {
        Long transId = null;
        String contractNo = rs.getString( "contract_no" );
        int overdueDays = rs.getInt( "current_absolute_overdue" );

        //表外金额
        BigDecimal outerAmt = rs.getBigDecimal( "outer_amt" ) != null ? rs.getBigDecimal( "outer_amt" ) : BigDecimal.ZERO;
        //摊销利息
        BigDecimal receivableInterest = rs.getBigDecimal( "receivable_interest" ) != null ? rs.getBigDecimal( "receivable_interest" ) : BigDecimal.ZERO;
        //总摊销利息
        BigDecimal interestIncome = rs.getBigDecimal( "interest_income" ) != null ? rs.getBigDecimal( "interest_income" ) : BigDecimal.ZERO;

        //表外金额等于0 且逾期天数<=90
        if (outerAmt.compareTo( BigDecimal.ZERO ) == 0 && overdueDays <= 90) {
            //确认利息收入
            if (receivableInterest.compareTo( BigDecimal.ZERO ) != 0) {
                transId = uidGenerator.getUID();
                handleInterestIncome( psf, transId, "interestIncome", contractNo, receivableInterest, VoucherBuriedPointNo.recognitionInterestIncome, batchDate );
            }
            //更新暂记账
            handleSuspenseAccount( updatePsf, contractNo, BigDecimal.ZERO,BigDecimal.ZERO,receivableInterest );
        }
        //表外金额等于0 且逾期天数>90
        else if (outerAmt.compareTo( BigDecimal.ZERO ) == 0 && overdueDays > 90) {
            BigDecimal interest = getInterest( contract, contractNo );//
           BigDecimal   receivable  = interestIncome.subtract( interest).add( receivableInterest );
            //转移利息收入到暂记账
            transId = uidGenerator.getUID();
            handleInterestIncome( psf, transId, "transferInterestToSuspense", contractNo, receivable, VoucherBuriedPointNo.transferInterestToSuspense, batchDate );

            //转移应收利息到暂记账
            transId = uidGenerator.getUID();
            handleInterestIncome( psf, transId, "transferReceivableToSuspense", contractNo, receivable.negate(), VoucherBuriedPointNo.transferReceivableToSuspense, batchDate );
            //更新暂记账
            handleSuspenseAccount( updatePsf, contractNo, receivable,receivableInterest,receivableInterest );

            //确认暂记应收利息
            if (receivableInterest.compareTo( BigDecimal.ZERO ) != 0) {
                transId = uidGenerator.getUID();
                handleInterestIncome( psf, transId, "suspenseInterestReceivable", contractNo, receivableInterest.negate(), VoucherBuriedPointNo.suspenseInterestReceivable, batchDate );
            }
            //确认暂记利息收入
            if (receivableInterest.compareTo(BigDecimal.ZERO) != 0) {
                transId = uidGenerator.getUID();
                handleInterestIncome(psf, transId, "suspenseInterest", contractNo, receivableInterest, VoucherBuriedPointNo.recognitionSuspenseInterest,batchDate);
            }
        }  //表外金额等于0 且逾期天数>90
        else if (outerAmt.compareTo( BigDecimal.ZERO ) > 0) {
            //确认暂记应收利息
            if (receivableInterest.compareTo( BigDecimal.ZERO ) != 0) {
                transId = uidGenerator.getUID();
                handleInterestIncome( psf, transId, "suspenseInterestReceivable", contractNo, receivableInterest.negate(), VoucherBuriedPointNo.suspenseInterestReceivable, batchDate );
            }
            //确认暂记利息收入
            if (receivableInterest.compareTo(BigDecimal.ZERO) != 0) {
                transId = uidGenerator.getUID();
                handleInterestIncome(psf, transId, "suspenseInterest", contractNo, receivableInterest, VoucherBuriedPointNo.recognitionSuspenseInterest,batchDate);
            }
            //更新暂记账
            handleSuspenseAccount( updatePsf, contractNo, receivableInterest,receivableInterest,receivableInterest );
        }
        return transId;
    }


    //获取断点id
    private Long getId(String loadBreakPoint, Connection contract) throws SQLException {
        if (StringUtil.isBlank( loadBreakPoint ))
            return null;
        StringBuffer sql = new StringBuffer();
        sql.append( "select id from  basic_business_accounting where trans_id=? " );
        PreparedStatement ps = contract.prepareStatement( sql.toString() );
        ResultSet rs = null;
        Long id = null;
        JSONObject jsonObject = JSONObject.parseObject( loadBreakPoint );
        Long startId = (Long) jsonObject.get( "startId" );
        Long endId = (Long) jsonObject.get( "endId" );
        Long targetId = (Long) jsonObject.get( "targetId" );
        if (targetId != null) {
            ps.setLong( 1, targetId );
            rs = ps.executeQuery();
            if (rs.next()) {
                id = endId;
            } else {
                id = startId;
            }
        }
        clean( ps );
        clean( rs );
        return id;
    }


    //获取应收利息
    private BigDecimal getInterest(Connection contract, String contractNo) throws SQLException {
        StringBuffer sqlInterestIncome = new StringBuffer();
        sqlInterestIncome.append( " select sum(IFNULL(actual_interest,0)) as actual_interest  from  basic_repayment_plan cost where  cost.contract_no=?  " );
        ResultSet rsInterestIncome = null;
        PreparedStatement psInterestIncome = contract.prepareStatement( sqlInterestIncome.toString() );
        psInterestIncome.setString( 1, contractNo );
        rsInterestIncome = psInterestIncome.executeQuery();
        BigDecimal actualInterest = null;
        while (rsInterestIncome != null && rsInterestIncome.next()) {
            actualInterest = rsInterestIncome.getBigDecimal( 1 ) != null ? rsInterestIncome.getBigDecimal( 1 ) : BigDecimal.ZERO;
        }
        clean( rsInterestIncome );
        clean( psInterestIncome );
        return actualInterest;
    }


}